<?php

/**
 * Provides the database connection to the Models.
 * @package Nina
 * @author John Main <john@thedaddy.org> Chris Corbyn
 */
abstract class Nina_Model extends Nina_Orm
{
	/**
	 * Constructor
	 */
	public function __construct() {
		parent::__construct($this->getDb());
	}

	/**
	 * Gets the database instance.
	 * @return Nina_Db
	 */
	public function getDb() {
		$reg = Nina_Registry::getInstance();
		return $reg->get("database.master.instance");

		/*
		$reg = Nina_Registry::getInstance();
		$db = $reg->get("database.master.instance");
		$db->init($reg->getReference('database.master'));
		return $db;
		*/
	}

	/**
	 * Check if this is a dummy record
	 * @return boolean
	 */
	public function isDummy() {
		return false;
	}
	
	/**
	 * Wrapper for find functionality
	 * @param Nina_Orm_Criteria [$criteria=null] The search criteria
	 * @return array
	 */
	public function find($criteria = null) {
		//This will do a classic find all
		if(!$criteria)
			$criteria = $this->newCriteria();
		return Nina_Orm::doSelect($this, $criteria);
	}	
	
	
	/**
	 * Wrapper for find all functionality
	 * @param Nina_Orm_Criteria [$criteria=null] The search criteria
	 * @return array
	 */
	public function findAll($criteria = null) {
		return $this->find($criteria);
	}
	
	/**
	 * Wrapper for find one functionality
	 * @param Nina_Orm_Criteria [$criteria=null] The search criteria
	 * @return Nina_Orm
	 */
	public function findOne($criteria = null) {
		if($criteria) {
			$criteria->setLimit(1);
			$result = $this->find($criteria);
			return ($result) ? $result[0] : false;
		}
		else
			return $this->find_first();
	}
	
	/**
	 * Wrapper for find first matching record functionality
	 * @param Nina_Orm_Criteria [$criteria=null] The search criteria
	 * @return Nina_Orm
	 */
	public function findFirst($criteria = null) {
		if(!$criteria)
			$criteria = $this->newCriteria();

		$criteria->addOrderBy($this->_getPkField(), Nina_Orm_Criteria::ORDER_ASC);
		$criteria->setLimit(1);
		return Nina_Orm::doSelectOne($this, $criteria);
	}
	
	/**
	 * Wrapper for find last matching record functionality
	 * @param Nina_Orm_Criteria [$criteria=null] The search criteria
	 * @return Nina_Orm
	 */
	public function findLast($criteria = null) {
		if(!$criteria)
			$criteria = $this->newCriteria();

		$criteria->addOrderBy($this->_getPkField(), Nina_Orm_Criteria::ORDER_DESC);
		$criteria->setLimit(1);
		return Nina_Orm::doSelectOne($this, $criteria);
	}

	/**
	 * Wrapper for find by field value functionality
	 * @param string|array $field Field name(s)
	 * @param mixed [$value=null] Field Value
	 * @return array
	 */
	public function findBy($field, $value = null) {
		if(is_array($field)) {	
			$c = $this->newCriteria();
				
			foreach($field as $field => $value) {
				if($this->hasField($field) && $value)
					$c->add($this->getField($field), $value);
				elseif($this->hasColumn($field) && $value)
					$c->add($field, $value);
			}
			
			return $this->find($c);
		}
		else {
			if($this->hasField($field) && $value) {
				$c = new Nina_Orm_Criteria();
				$c->add($this->getField($field), $value);
				return $this->find($c);
			}
			elseif($this->hasColumn($field) && $value) {
				$c = new Nina_Orm_Criteria();
				$c->add($field, $value);
				return $this->find($c);
			}
		}
	}

	/**
	 * Wrapper for find one by field value functionality
	 * @param string|array $field Field name(s)
	 * @param mixed [$value=null] Field Value
	 * @return array
	 */
	public function findOneBy($field, $value = null) {
		if(is_array($field)) {	
			$c = $this->newCriteria();
				
			foreach($field as $field => $value) {
				if($this->hasField($field) && $value)
					$c->add($this->getField($field), $value);
				elseif($this->hasColumn($field) && $value)
					$c->add($field, $value);
			}
			
			return $this->findOne($c);
		}
		else {
			if($this->hasField($field) && $value) {
				$c = new Nina_Orm_Criteria();
				$c->add($this->getField($field), $value);
				return $this->findOne($c);
			}
			elseif($this->hasColumn($field) && $value) {
				$c = new Nina_Orm_Criteria();
				$c->add($field, $value);
				return $this->findOne($c);
			}
		}
	}
	
	/**
	 * This works the same as find_one(), except that if the first record is not found, a new one is created according to the given criteria and returned
	 * @param Nina_Orm_Criteria $criteria The search criteria
	 * @return Nina_Orm
	 * @see doSelect
	 */
	public function findOrCreate(Nina_Orm_Criteria $criteria) {
		if($record = $this->find_one($criteria)) {
			return $record;
		}
		else {
			$fields = $criteria->getFields();
			
			//Making a new one as we want to return the newly inserted record
			$obj = $this->doClone();

			//Can only work with 'where', not 'having'
			foreach ($fields['where'] as $field) {
				list($model, $field_name) = explode('.', $field['field']);

				if($this->hasField($field_name)) {
					//Can only work with 'and', not 'or'
					if($field['andor'] == Nina_Orm_Criteria::OP_AND) {
						$method = 'set'.Nina_String::camelise($field_name);
						$obj->$method($field['value']);
					}
				}
			}

			$obj->save();

			return $obj;
		}
	}

	/**
	 * Create a new (unsaved) record from a list of values.
	 * @param array $fields Fields mapped as (field => value)
	 * @param boolean [$sanitise=false] Sanitise data
	 * @return Nina_Orm
	 */
	public function create(Array $fields, $sanitise = false) {	
		return Nina_Orm::doInsert($this, $fields, $sanitise);
	}

	/**
	 * Update a record to set the given fields where the primary key matches the one given.
	 * @param array $fields Fields to set (field => value)
	 * @param Nina_Orm_Criteria $criteria The search criteria
	 * @param boolean [$show_deleted=false] Show deleted records
	 * @param boolean [$sanitise=false] Sanitise data
	 * @return boolean
	 */
	public function do_update(array $fields, $criteria = null, $show_deleted = false, $sanitise = false) {
		return Nina_Orm::doUpdate($this, $fields, $show_deleted, $sanitise);
	}

	/**
	 * Create or update a record from the given list of values.
	 * @param array $fields Fields to set (field => value)
	 * @param boolean [$show_deleted=false] Show deleted records
	 * @param boolean [$sanitise=false] Sanitise data
	 * @return Nina_Orm
	 */
	public function do_replace(array $fields, $show_deleted = false, $sanitise = false) {
		return Nina_Orm::doReplace($this, $fields, $show_deleted, $sanitise);
	}

	/**
	 * Delete records which match the given criteria.
	 * @param Nina_Orm_Criteria [$criteria=null] The criteria to search
	 * @return boolean
	 */
	public function do_delete($criteria = null) {
		return Nina_Orm::doDelete($this, $criteria);
	}
	
	/**
	 * Return the differences between two record sets, comparing given field or primary keys.
	 * @param array $set1 The first record set
	 * @param array $set2 The second record set
	 * @param array [$compare='Pk'] The field to compare (camel cased)
	 * @return array
	 */
	public static function compareRecordSets($set1, $set2, $compare = 'Pk') {
	
		$method = 'get'.ucfirst($compare);

		$set1_keys = array();
		foreach($set1 as $record1) {
			$set1_keys[] = $record1->$method();
		}

		$set2_keys = array();
		foreach($set2 as $record2) {
			$set2_keys[] = $record1->$method();
		}
		
		return array_diff($set1_keys, $set2_keys);
	}
}
